package com.boguan.web.controller.mqtt;

import com.boguan.common.core.redis.RedisCache;

import com.boguan.device.domain.*;

import com.boguan.device.domain.vo.DeviceListVo;
import com.boguan.device.mapper.DeviceMapper;

import com.boguan.device.service.IDeviceService;

import com.boguan.web.controller.tool.CrcUtils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;



/**
 * mqtt业务处理类
 * mqttCallback
 *
 * @author chen
 * @date 2019/12/10
 */
@Component
public class PushCallback implements MqttCallback {

    private static final Logger logger = LoggerFactory.getLogger(PushCallback.class);

    @Lazy
    @Autowired
    private MqttPushClient mqttPushClient;

    @Lazy
    @Autowired
    private MqttConfiguration mqttConfiguration;

    @Autowired
    private IDeviceService deviceService;

    @Autowired
    DeviceMapper deviceMapper;
    @Autowired
    RedisCache redisCache;

    private final static String sendTopic ="qianbiao/s/qianbiao/";


    private BigDecimal a = new BigDecimal(1000);

    private BigDecimal curA = BigDecimal.ZERO;
    private BigDecimal curB = BigDecimal.ZERO;
    private BigDecimal curC = BigDecimal.ZERO;
    private BigDecimal curD = BigDecimal.ZERO;

    private BigDecimal rateA = BigDecimal.ZERO;
    private BigDecimal rateB = BigDecimal.ZERO;
    private BigDecimal rateC = BigDecimal.ZERO;
    private BigDecimal rateD = BigDecimal.ZERO;



    @Override
    public void connectionLost(Throwable throwable) {
        // 连接丢失后，一般在这里面进行重连
        logger.info("[MQTT] 连接断开，3S之后尝试重连...");
        while (true) {
            try {
                Thread.sleep(3000);
                mqttPushClient.reConnect();
                logger.info("[MQTT] 链接成功...");
                break;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        mqttPushClient.subscribe(mqttConfiguration.getTopic());//监听设备信息
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) {
        // subscribe后得到的消息会执行到这里面
        logger.info("主题：{}", topic);
        //logger.info("内容：{}", new String(message.getPayload()));
        new Thread(() -> messageProcessing(topic, message)).start();
    }

    /**
     * 消息处理方法
     *
     * @param topic   topic主题
     * @param message 消息内容
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized void messageProcessing(String topic, MqttMessage message) {

        String[] split = topic.split("/");
        String deviceNum = split[3];
        Device device = deviceService.selectDeviceByDeviceNum(deviceNum, 1);

        Date dataTime = new Date();

        byte[] bytes = message.getPayload();
        if(deviceNum.startsWith("0")){
            return;
        }
          curA = BigDecimal.ZERO;
          curB = BigDecimal.ZERO;
          curC = BigDecimal.ZERO;
          curD = BigDecimal.ZERO;

          rateA = BigDecimal.ZERO;
          rateB = BigDecimal.ZERO;
          rateC = BigDecimal.ZERO;
          rateD = BigDecimal.ZERO;

        String b = analysis(bytes, 3, 1);
        StringBuilder hex = new StringBuilder();
        if("01".equals(b)){
           hex.append("5a");
           hex.append(String.format("%04x",11));
           hex.append("01");
           hex.append("10");
           hex.append(String.format("%08x",dataTime.getTime()/1000));
           String crc3 = CrcUtils.getCRC3(hexString2Bytes(hex.toString()));
           hex.append(reverse(crc3));
           mqttPushClient.publish(sendTopic+deviceNum,hexString2Bytes(hex.toString()));

           String imei = hexStringToDesc(analysis(bytes, 4, 15));
           String iccid = hexStringToDesc(analysis(bytes, 19, 20));
           Long csq = Long.valueOf(analysis(bytes, 20, 1), 16);


           if (device == null) {
               device = new Device();
               device.setDeviceNum(deviceNum);
               device.setImei(imei);
               device.setIccid(iccid);
               device.setCsq(csq.toString());
               deviceMapper.insertSelective(device);
               return;
           }
           device.setImei(imei);
           device.setIccid(iccid);
           device.setCsq(csq.toString());
           deviceMapper.updateByPrimaryKeySelective(device);
        }else if("02".equals(b)){
//            byte[] a = new byte[bytes.length-2];
//            System.arraycopy(bytes,0,a,0,bytes.length-2);
//            String reverse = reverse(CrcUtils.getCRC3(a));
//            String analysis = analysis(bytes, 0, bytes.length);
//            if(!analysis.substring(analysis.length()-4).equals(reverse)){
//                return;
//            }

           hex.append("5a");
           hex.append(String.format("%04x",11));
           hex.append("02");
           hex.append("11");
           hex.append(String.format("%08x",dataTime.getTime()/1000));
           String crc3 = CrcUtils.getCRC3(hexString2Bytes(hex.toString()));
           hex.append(reverse(crc3));
           mqttPushClient.publish(sendTopic+deviceNum,hexString2Bytes(hex.toString()));

            long currentA = Long.valueOf(analysis(bytes, 9, 2), 16);
            logger.info("电流1 :"+currentA);
            long currentB = Long.valueOf(analysis(bytes, 24, 2), 16);
            logger.info("电流2 :"+currentB);
            long currentC = Long.valueOf(analysis(bytes, 39, 2), 16);
            logger.info("电流3 :"+currentC);
            long currentD = Long.valueOf(analysis(bytes, 54, 2), 16);
            logger.info("电流4 :"+currentD);

            long curBit = Long.valueOf(analysis(bytes, 12, 1), 16);

            String curUnit1 = analysis(bytes, 11, 1);
            String curUnit2 = analysis(bytes, 26, 1);
            String curUnit3 = analysis(bytes, 41, 1);
            String curUnit4 = analysis(bytes, 56, 1);

            long pinlvA = Long.valueOf(analysis(bytes, 13, 2), 16);
            long pinlvB = Long.valueOf(analysis(bytes, 28, 2), 16);
            long pinlvC = Long.valueOf(analysis(bytes, 43, 2), 16);
            long pinlvD = Long.valueOf(analysis(bytes, 58, 2), 16);

            String rateUnit1 =analysis(bytes, 15, 1);
            String rateUnit2 =analysis(bytes, 30, 1);
            String rateUnit3 =analysis(bytes, 45, 1);
            String rateUnit4 =analysis(bytes, 60, 1);

            long rateBit = Long.valueOf(analysis(bytes, 16, 1), 16);

            Long time1 = Long.valueOf(analysis(bytes, 5, 4),16);

            Long time2 = Long.valueOf(analysis(bytes, 20, 4),16);

            Long time3 = Long.valueOf(analysis(bytes, 35, 4), 16);

            Long time4 = Long.valueOf(analysis(bytes, 50, 4), 16);
            Date d4 = new Date(time4*1000);
            Date d3 = new Date(time3*1000);
            Date d2 = new Date(time2*1000);
            Date d1 = new Date(time1*1000);


            getCurrent((int)curBit,currentA, currentB, currentC, currentD, curUnit1);
            getCurrent((int)curBit,currentA, currentB, currentC, currentD, curUnit2);
            getCurrent((int)curBit,currentA, currentB, currentC, currentD, curUnit3);
            getCurrent((int)curBit,currentA, currentB, currentC, currentD, curUnit4);


            getRate((int)rateBit,pinlvA, pinlvB, pinlvC, pinlvD, rateUnit1);
            getRate((int)rateBit,pinlvA, pinlvB, pinlvC, pinlvD, rateUnit2);
            getRate((int)rateBit,pinlvA, pinlvB, pinlvC, pinlvD, rateUnit3);
            getRate((int)rateBit,pinlvA, pinlvB, pinlvC, pinlvD, rateUnit4);



            getDeviceListVo(device, d1,curA,rateA);
            getDeviceListVo(device, d2,curB,rateB);
            getDeviceListVo(device, d3,curC,rateC);
            DeviceListVo deviceListVo = getDeviceListVo(device, d4, curD, rateD);

            deviceMapper.updateDeviceInfo(deviceListVo);

        }else if("03".equals(b)){
            hex.append("5a");
            hex.append(String.format("%04x",11));
            hex.append("03");
            hex.append("12");
            hex.append(String.format("%08x",dataTime.getTime()/1000));
            String crc3 = CrcUtils.getCRC3(hexString2Bytes(hex.toString()));
            hex.append(reverse(crc3));
            mqttPushClient.publish(sendTopic+deviceNum,hexString2Bytes(hex.toString()));

            long currentA = Long.valueOf(analysis(bytes, 8, 2), 16);
            String curUnit = analysis(bytes, 10, 1);
            Long curBit = Long.valueOf(analysis(bytes, 11, 1), 16);
            long pinlvA =  Long.valueOf(analysis(bytes, 12, 2), 16);
            String rateUnit = analysis(bytes, 14, 1);
            Long rateBit = Long.valueOf(analysis(bytes, 15, 1), 16);
            Long time = Long.valueOf(analysis(bytes, 4, 4),16);
            Date d1 = new Date(time*1000);
            logger.info("电流 :"+currentA);
            switch (curUnit) {
                case "14":
                    setValue(curBit.intValue(),currentA,0L,0L,0L);
                    logger.info("电流：{}", curA + "mA");
                    curA = curA.divide(a,6, BigDecimal.ROUND_HALF_UP);
                    break;
                case "24":
                    setValue(curBit.intValue(),0L,0L,0L);
                    logger.info("电流：{}", curA + "A");
                    break;
                case "34":
                    setValue(curBit.intValue(),0L,0L,0L);
                    logger.info("电流：{}", curA + "kA");
                    curA = curA.multiply(a);
                    break;
            }

            switch (rateUnit) {
                case "18":
                    logger.info("频率1：{}", pinlvA + "mHz");
                    setRate(rateBit.intValue(),pinlvA,0L,0L,0L);
                    rateA= rateA.divide(a,6, BigDecimal.ROUND_HALF_UP);
                    break;
                case "28":
                    logger.info("频率1：{}", pinlvA + "Hz");
                    setRate(rateBit.intValue(),pinlvA,0L,0L,0L);
                    break;
                case "38":
                    logger.info("频率1：{}", pinlvA + "kHz");
                    rateA = rateA.multiply(a);
                    break;
            }
            DeviceListVo deviceListVo = new DeviceListVo();
            deviceListVo.setDevice_id(device.getDeviceId().toString());
            deviceListVo.setElectric_current(curA.toString());
            deviceListVo.setRate(rateA.toString());
            deviceListVo.setCreate_time(d1);
            deviceMapper.insertDeviceData(deviceListVo);
            deviceMapper.updateDeviceInfo(deviceListVo);

        }


    }

    private DeviceListVo getDeviceListVo(Device device, Date d4,BigDecimal cur,BigDecimal rate) {
        DeviceListVo deviceListVo = new DeviceListVo();
        deviceListVo.setDevice_id(device.getDeviceId().toString());
        deviceListVo.setElectric_current(cur.toString());
        deviceListVo.setRate(rate.toString());
        deviceListVo.setCreate_time(d4);
        deviceMapper.insertDeviceData(deviceListVo);
        return deviceListVo;

    }

    private void getCurrent(Integer curBit, long currentA, long currentB, long currentC, long currentD, String curUnit1) {
        switch (curUnit1) {
            case "14":
                setValue(curBit,currentA,currentB,currentC,currentD);
                logger.info("电流：{}", curA + "mA");
                curA = curA.divide(a,6, BigDecimal.ROUND_HALF_UP);
                curB = curB.divide(a,6, BigDecimal.ROUND_HALF_UP);
                curC = curC.divide(a,6, BigDecimal.ROUND_HALF_UP);
                curD = curD.divide(a,6, BigDecimal.ROUND_HALF_UP);
                break;
            case "24":
                setValue(curBit,currentA,currentB,currentC,currentD);
                logger.info("电流：{}", curA + "A");
                break;
            case "34":
                setValue(curBit,currentA,currentB,currentC,currentD);
                logger.info("电流：{}", curA + "kA");
                curA = curA.multiply(a);
                curB = curB.multiply(a);
                curC = curC.multiply(a);
                curD = curD.multiply(a);
                break;
        }
    }

    private void getRate(Integer rateBit,long pinlvA, long pinlvB, long pinlvC, long pinlvD, String rateUnit4) {
        switch (rateUnit4) {
            case "18":
                logger.info("频率1：{}", pinlvA + "mHz");
                setRate(rateBit,pinlvA,pinlvB,pinlvC,pinlvD);
                rateA = rateA.divide(a,6, BigDecimal.ROUND_HALF_UP);
                rateB = rateB.divide(a,6, BigDecimal.ROUND_HALF_UP);
                rateC = rateC.divide(a,6, BigDecimal.ROUND_HALF_UP);
                rateD = rateD.divide(a,6, BigDecimal.ROUND_HALF_UP);
                break;
            case "28":
                logger.info("频率1：{}", pinlvA + "Hz");
                setRate(rateBit,pinlvA,pinlvB,pinlvC,pinlvD);
                break;
            case "38":
                logger.info("频率1：{}", pinlvA + "kHz");
                setRate(rateBit,pinlvA,pinlvB,pinlvC,pinlvD);
                rateA = rateA.multiply(a);
                rateB = rateB.multiply(a);
                rateC = rateC.multiply(a);
                rateD = rateD.multiply(a);
                break;
        }
    }


    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        logger.info("发送消息：{}", iMqttDeliveryToken.isComplete());
    }


    /**
     * 解析数据
     */
    public static String analysis(byte[] bytes, int srcpos, int length) {
        byte[] bstr = new byte[length];
        try {
            System.arraycopy(bytes, srcpos, bstr, 0, length);
        } catch (Exception e) {
            System.out.println(srcpos + " " + length);
            System.out.println(bytesToHexString(bytes));
        }

        String str = bytesToHexString(bstr);
        return str;
    }

    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    public static byte[] hexString2Bytes(String hex) {

        if ((hex == null) || (hex.equals(""))) {
            return null;
        } else if (hex.length() % 2 != 0) {
            return null;
        } else {
            hex = hex.toUpperCase();
            int len = hex.length() / 2;
            byte[] b = new byte[len];
            char[] hc = hex.toCharArray();
            for (int i = 0; i < len; i++) {
                int p = 2 * i;
                b[i] = (byte) (charToByte(hc[p]) << 4 | charToByte(hc[p + 1]));
            }
            return b;
        }
    }

    //把16进制字符串转换成字节数组
    public static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }


    public static String reverse(String crc3){
        crc3=crc3.substring(crc3.length()-4);
        String end =crc3.substring(0,2);
        String result=crc3.substring(crc3.length()-2)+end;
        return result.toLowerCase();
    }

    public static String hexStringToDesc(String hex) {
        StringBuilder output = new StringBuilder();
        for (int i = 0; i < hex.length(); i += 2) {
            String str = hex.substring(i, i + 2);
            if (!"00".equals(str)) {
                output.append((char) Integer.parseInt(str, 16));
            }
        }

        return output + "";
    }



    private void setValue(Integer bitUnit,Long... current){
        curA = new BigDecimal(current[0]).movePointLeft(bitUnit);
        curB = new BigDecimal(current[1]).movePointLeft(bitUnit);
        curC = new BigDecimal(current[2]).movePointLeft(bitUnit);
        curD = new BigDecimal(current[3]).movePointLeft(bitUnit);
    }

    private void setRate(Integer bitUnit,Long... rate){
        rateA = new BigDecimal(rate[0]).movePointLeft(bitUnit);
        rateB = new BigDecimal(rate[1]).movePointLeft(bitUnit);
        rateC = new BigDecimal(rate[2]).movePointLeft(bitUnit);
        rateD = new BigDecimal(rate[3]).movePointLeft(bitUnit);
    }



}
